/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package javagameengine;

import java.awt.geom.AffineTransform;

/**
 *
 * @author jimi
 */
public class Matrix
{

	double elements[][] = new double[4][4];
	public static final Matrix Identity = new Matrix();

	public Matrix()
	{
		int i, j;

		for( i = 0 ; i < 4 ; i++ )
		{
			for( j = 0 ; j < 4 ; j++ )
			{
				elements[i][j] = 0;

				if( i == j )
				{
					elements[i][j] = 1;
				}
			}
		}
	}

	public Matrix( Matrix other )
	{
		int i, j;

		for( i = 0 ; i < 4 ; i++ )
		{
			for( j = 0 ; j < 4 ; j++ )
			{
				elements[i][j] = other.elements[i][j];
			}
		}
	}

	public double get( int i, int j )
	{
		return elements[i][j];
	}

	public Vector times( Vector u )
	{
		Vector v = new Vector();

		v.x = u.x * get( 0, 0 ) + u.y * get( 0, 1 ) + u.z * get( 0, 2 ); // + get(0,3) ; vectors
		v.y = u.x * get( 1, 0 ) + u.y * get( 1, 1 ) + u.z * get( 1, 2 ); // + get(1,3) ; cant be
		v.z = u.x * get( 2, 0 ) + u.y * get( 2, 1 ) + u.z * get( 2, 2 ); // + get(2,3) ; translated silly

		return v;
	}

	public Point times( Point u )
	{
		Point v = new Point();

		v.x = u.x * get( 0, 0 ) + u.y * get( 0, 1 ) + u.z * get( 0, 2 ) + get( 0, 3 );
		v.y = u.x * get( 1, 0 ) + u.y * get( 1, 1 ) + u.z * get( 1, 2 ) + get( 1, 3 );
		v.z = u.x * get( 2, 0 ) + u.y * get( 2, 1 ) + u.z * get( 2, 2 ) + get( 2, 3 );

		return v;
	}

	public AffineTransform to_affine()
	{
		AffineTransform at = new AffineTransform();

		at.setTransform( elements[0][0],
				elements[1][0],
				elements[0][1],
				elements[1][1],
				elements[0][2],
				elements[1][2] );

		return at;
	}

	public Matrix times( Matrix o )
	{
		Matrix m = new Matrix();

		/* row 1 */
		m.elements[0][0] = elements[0][0] * o.elements[0][0] + elements[0][1] * o.elements[1][0]
				+ elements[0][2] * o.elements[2][0] + elements[0][3] * o.elements[3][0];

		m.elements[0][1] = elements[0][0] * o.elements[0][1] + elements[0][1] * o.elements[1][1]
				+ elements[0][2] * o.elements[2][1] + elements[0][3] * o.elements[3][1];

		m.elements[0][2] = elements[0][0] * o.elements[0][2] + elements[0][1] * o.elements[1][2]
				+ elements[0][2] * o.elements[2][2] + elements[0][3] * o.elements[3][2];

		m.elements[0][3] = elements[0][0] * o.elements[0][3] + elements[0][1] * o.elements[1][3]
				+ elements[0][2] * o.elements[2][3] + elements[0][3] * o.elements[3][3];

		/* row 2 */
		m.elements[1][0] = elements[1][0] * o.elements[0][0] + elements[1][1] * o.elements[1][0]
				+ elements[1][2] * o.elements[2][0] + elements[1][3] * o.elements[3][0];

		m.elements[1][1] = elements[1][0] * o.elements[0][1] + elements[1][1] * o.elements[1][1]
				+ elements[1][2] * o.elements[2][1] + elements[1][3] * o.elements[3][1];

		m.elements[1][2] = elements[1][0] * o.elements[0][2] + elements[1][1] * o.elements[1][2]
				+ elements[1][2] * o.elements[2][2] + elements[1][3] * o.elements[3][2];

		m.elements[1][3] = elements[1][0] * o.elements[0][3] + elements[1][1] * o.elements[1][3]
				+ elements[1][2] * o.elements[2][3] + elements[1][3] * o.elements[3][3];

		/* row 3 */
		m.elements[2][0] = elements[2][0] * o.elements[0][0] + elements[2][1] * o.elements[1][0]
				+ elements[2][2] * o.elements[2][0] + elements[2][3] * o.elements[3][0];

		m.elements[2][1] = elements[2][0] * o.elements[0][1] + elements[2][1] * o.elements[1][1]
				+ elements[2][2] * o.elements[2][1] + elements[2][3] * o.elements[3][1];

		m.elements[2][2] = elements[2][0] * o.elements[0][2] + elements[2][1] * o.elements[1][2]
				+ elements[2][2] * o.elements[2][2] + elements[2][3] * o.elements[3][2];

		m.elements[2][3] = elements[2][0] * o.elements[0][3] + elements[2][1] * o.elements[1][3]
				+ elements[2][2] * o.elements[2][3] + elements[2][3] * o.elements[3][3];

		/* row 4 */
		m.elements[3][0] = elements[3][0] * o.elements[0][0] + elements[3][1] * o.elements[1][0]
				+ elements[3][2] * o.elements[2][0] + elements[3][3] * o.elements[3][0];

		m.elements[3][1] = elements[3][0] * o.elements[0][1] + elements[3][1] * o.elements[1][1]
				+ elements[3][2] * o.elements[2][1] + elements[3][3] * o.elements[3][1];

		m.elements[3][2] = elements[3][0] * o.elements[0][2] + elements[3][1] * o.elements[1][2]
				+ elements[3][2] * o.elements[2][2] + elements[3][3] * o.elements[3][2];

		m.elements[3][3] = elements[3][0] * o.elements[0][3] + elements[3][1] * o.elements[1][3]
				+ elements[3][2] * o.elements[2][3] + elements[3][3] * o.elements[3][3];

		return m;
	}

	public static Matrix translation( Vector v )
	{
		Matrix m = new Matrix();

		m.elements[0][3] = v.x;
		m.elements[1][3] = v.y;
		m.elements[2][3] = v.z;

		return m;
	}

	public Matrix minus( Matrix other )
	{
		Matrix m = new Matrix();

		int i, j;

		for( i = 0 ; i < 4 ; i++ )
		{
			for( j = 0 ; j < 4 ; j++ )
			{
				m.elements[i][j] = elements[i][j] - other.elements[i][j];
			}
		}

		return m;
	}

	public Matrix plus( Matrix other )
	{
		Matrix m = new Matrix();


		int i, j;

		for( i = 0 ; i < 4 ; i++ )
		{
			for( j = 0 ; j < 4 ; j++ )
			{
				m.elements[i][j] = elements[i][j] + other.elements[i][j];
			}
		}

		return m;
	}

	public Matrix times( double scalar )
	{
		Matrix m = new Matrix();
		int i, j;

		for( i = 0 ; i < 4 ; i++ )
		{
			for( j = 0 ; j < 4 ; j++ )
			{
				m.elements[i][j] = elements[i][j] * scalar;
			}
		}

		return m;
	}

	public static Matrix vector_times_vector_transposed( Vector v, Vector u )
	{
		Matrix m = new Matrix();

		m.elements[0][0] = v.x * u.x;
		m.elements[0][1] = v.x * u.y;
		m.elements[0][2] = v.x * u.z;

		m.elements[1][0] = v.y * u.x;
		m.elements[1][1] = v.y * u.y;
		m.elements[1][2] = v.y * u.z;

		m.elements[2][0] = v.z * u.x;
		m.elements[2][1] = v.z * u.y;
		m.elements[2][2] = v.z * u.z;

		return m;
	}

	public static Matrix projection_orthogonal( Vector n )
	{

		Matrix m;

		/*  I         -      n*nt / n.length^2  */
		m = Identity.minus( vector_times_vector_transposed( n, n ).times( 1.0 / n.magnitude_squared() ) );

		return m;
	}
	
	public static Matrix scale( Vector factor )
	{
		Matrix m = new Matrix();
		
		m.elements[0][0] = factor.x ;
		m.elements[1][1] = factor.y ;
		m.elements[2][2] = factor.z ;
		
		return m; 
	}
	
	public static Matrix projection_skew( Vector n, Vector u )
	{
		Matrix m;

		m = Identity.minus( vector_times_vector_transposed( u, n ).times( 1.0 / u.dot( n ) ) );

		return m;
	}

	public static Matrix pers()
	{
		Matrix m = new Matrix();

		m.elements[3][3] = 0;
		m.elements[3][2] = 1;

		return m;
	}

	/**
	 * BROKEN
	 *
	 * @param E
	 * @param X
	 * @param P
	 * @param n
	 * @return
	 */
	public static Point pers( Point E, Point X, Point P, Vector n )
	{
		Point ret;

		// ret = E.plus( E.times( ((P.minus(E).dot(n)) / (X.minus(E).dot(n)) ) ) ) ;
		Vector EP = P.minus( E );
		Vector EX = X.minus( E );

		ret = E.plus( EX.project( EP ) );


		return ret;
	}

	public void print_matrix()
	{
		int i, j;

		for( i = 0 ; i < 4 ; i++ )
		{
			for( j = 0 ; j < 4 ; j++ )
			{
				System.out.print( i + " " + j + " is |" + elements[i][j] + "|" );
			}
			System.out.println();
		}
	}
}
